#include "SymbolTable.h"

void Sym_array::get_const_index()
{
    int ans = (*((this->array_range).begin())).first;
    auto it = ((this->array_range).begin());
    for(it++; it != ((this->array_range).end()); it++){
        int n = (*it).second - (*it).first + 1;
        ans = ans * n + (*it).first;
    }
    this->C = ans * this->w;
}

// int Sym_array::get_index(vector<int>& V)
// {
//     if(V.size() != this->array_range.size()){
//         return -1;
//     }
//     int ans = V[0];
//     int num = (int) V.size();
//     for(int i = 1; i < num; i++){
//         int low = this->array_range[i].first;
//         int high = this->array_range[i].second;
//         if(i < low || i > high){
//             return -1;
//         }
//         int n = high - low + 1;
//         ans = ans * n + V[i];
//     }
//     return ans * this->w - this->C;
// }

void SymbolTable::insert_ConstVariable(string id, int lineNumber, TYPE type, bool isMinus, string value)
{
    Sym_constVariable* sym_const_variable = new Sym_constVariable();
    sym_const_variable->name = id;
    sym_const_variable->typeName = type;
    sym_const_variable->is_minus = isMinus;
    sym_const_variable->line = lineNumber;
    if(sym_const_variable->is_minus){
        if(value.c_str()[0] == '-'){
            sym_const_variable->value = value.substr(1);
        }
        sym_const_variable->value = "-" + value;
    }
    else{
        sym_const_variable->value = value;
    }
    string N = "n_";
    if(type == TYPE::BOOL){
        if(sym_const_variable->value == "true"){
            N = N + "1";
        }
        else{
            N = N + "0";
        }
    }
    else if(type == TYPE::CHAR){
        int num = (int) sym_const_variable->value.c_str()[0];
        N = N + to_string(num);
    }
    else if(type == TYPE::FLOAT || type == TYPE::INT){
        N = N + sym_const_variable->value;
    }
    sym_const_variable->nameInComputer = N;
    this->constVariableList.push_back(sym_const_variable);
    PCI pci = make_pair(CLASS::CONST, (int)(this->constVariableList.size() - 1));
    this->fastPosition.insert(make_pair(id, pci));
}

void SymbolTable::insert_Variable(string id, int lineNumber, TYPE type)
{
    Sym_variable* sym_variable = new Sym_variable();
    sym_variable->name = id;
    sym_variable->line = lineNumber;
    sym_variable->typeName = type;
    string V = "v_";
    V = V + id + "_";
    V = V + this->head;
    sym_variable->nameInComputer = V;
    this->variableList.push_back(sym_variable);
    PCI pci = make_pair(CLASS::VARIABLE, (int)(this->variableList.size() - 1));
    this->fastPosition.insert(make_pair(id, pci));
}

void SymbolTable::insert_Array(string id, int lineNumber, TYPE type, vector<PII>& arrayRangeList)
{
    Sym_array* sym_array = new Sym_array();
    sym_array->name = id;
    sym_array->typeName = type;
    sym_array->line = lineNumber;
    sym_array->type = "standard";
    for(auto it = arrayRangeList.begin(); it != arrayRangeList.end(); it++){
        if((*it).first < 0){
            int number = 0 - (*it).first;
            sym_array->pos.push_back(number);
            int left = (*it).first + number;
            int right = (*it).second + number;
            sym_array->array_range.push_back(make_pair(left, right));
        }
        else{
            sym_array->array_range.push_back(*it);
            sym_array->pos.push_back(0);
        }
    }
    if(sym_array->typeName == TYPE::BOOL){
        sym_array->w = sizeof(bool);
    }
    else if(sym_array->typeName == TYPE::CHAR){
        sym_array->w = sizeof(char);
    }
    else if(sym_array->typeName == TYPE::INT){
        sym_array->w = sizeof(int);
    }
    else if(sym_array->typeName == TYPE::FLOAT){
        sym_array->w = sizeof(double);
    }
    sym_array->get_const_index();
    string A = "A_";
    A = A + id + "_";
    A = A + this->head;
    sym_array->nameInComputer = A;
    this->arrayList.push_back(sym_array);
    PCI pci = make_pair(CLASS::ARRAY, (int)(this->arrayList.size() - 1));
    this->fastPosition.insert(make_pair(id, pci));
}

// void SymbolTable::insert_Array_Record(string id, int lineNumber, SymRecordType* record, vector<PII>& arrayRangeList)
// {
//     Sym_array* sym_array = new Sym_array();
//     sym_array->name = id;
//     sym_array->record_struct = record;
//     sym_array->line = lineNumber;
//     sym_array->type = "record";
//     for(auto it = arrayRangeList.begin(); it != arrayRangeList.end(); it++){
//         if((*it).first < 0){
//             int number = 0 - (*it).first;
//             sym_array->pos.push_back(number);
//             int left = (*it).first + number;
//             int right = (*it).second + number;
//             sym_array->array_range.push_back(make_pair(left, right));
//         }
//         else{
//             sym_array->array_range.push_back(*it);
//             sym_array->pos.push_back(0);
//         }
//     }
//     sym_array->w = sym_array->record_struct->get_size();
//     sym_array->get_const_index();
//     string A = "v_";
//     A = A + "(" + id + ")" + "_";
//     A = A + "(" + this->head + ")";
//     sym_array->nameInComputer = A;
//     this->arrayList.push_back(sym_array);
//     PCI pci = make_pair(CLASS::ARRAY, (int)(this->arrayList.size() - 1));
//     this->fastPosition.insert(make_pair(id, pci));
// }

void SymbolTable::insert_record(string id, int lineNumber, SymRecordType* Type)
{
    Sym_Record* sym_record = new Sym_Record();
    sym_record->name = id;
    sym_record->line = lineNumber;
    sym_record->record_type = Type;
    string R = "R_";
    R = R + id + "_";
    R = R + this->head;
    sym_record->nameInComputer = R;
    this->recordList.push_back(sym_record);
    PCI pci = make_pair(CLASS::RECORD, (int)(this->recordList.size() - 1));
    this->fastPosition.insert(make_pair(id, pci));   
}

void SymbolTable::insert_return_value(TYPE type, int line)
{
    this->returnValue.typeName = type;
    this->returnValue.line = line;
    string R = "RE_";
    R = R + this->head;
    this->returnValue.nameInComputer = R;
}

void SymbolTable::insert_model_name(string id, int line)
{
    this->head = id;
    this->line = line;
}

void SymbolTable::insert_parameter(string id, int lineNumber, TYPE type, bool is_reference)
{
    Sym_parameter* sym_parameter = new Sym_parameter();
    sym_parameter->name = id;
    sym_parameter->typeName = type;
    sym_parameter->isReference = is_reference;
    string V = "p_";
    V = V + id + "_";
    V = V + this->head;
    sym_parameter->nameInComputer = V;
    this->parameterList.push_back(sym_parameter);
}

void SymbolTable::insert_function(SymbolTable* sub_symboltable)
{
    this->functionList.push_back(sub_symboltable);
    PCI pci = make_pair(CLASS::FUNCTION, (int)(this->functionList.size() - 1));
    this->fastPosition.insert(make_pair(sub_symboltable->head, pci));
}

void SymbolTable::insert_procedure(SymbolTable* sub_symboltable)
{
    this->procedureList.push_back(sub_symboltable);
    PCI pci = make_pair(CLASS::PROCEDURE, (int)(this->procedureList.size() - 1));
    this->fastPosition.insert(make_pair(sub_symboltable->head, pci));
}

bool SymbolTable::rename_head(string name)
{
    SymbolTable* now = this;
    while(now->preSymbolTable != nullptr){
        int res = name.compare(now->head);
        if(res == 0){
            return true;          
        }
        else{
            now = now->preSymbolTable; 
        }
    }
    return false;
}

CLASS SymbolTable::find_ID(string name)//就在本表找
{
    SymbolTable* now = this;
    auto it = now->fastPosition.find(name);
    if(it == now->fastPosition.end()){
        return CLASS::NEXIST;
    }
    else{
        return (*it).second.first;
    }
}

CLASS SymbolTable::find_all_ID(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            return (*it).second.first;
        }
    }
    return CLASS::NEXIST;
}

PTS SymbolTable::find_ConstVariable_value(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            CLASS Class = (*it).second.first;
            if(Class != CLASS::CONST){
                return make_pair(TYPE::NEXIST, "");
            }
            int id = (*it).second.second;
            Sym_constVariable* constvariable = now->constVariableList[id];
            TYPE type = constvariable->typeName;
            string value = constvariable->value;
            PTS ans = make_pair(type, value);
            return ans;
        }
    }  
    return make_pair(TYPE::NEXIST, "");
}

PTS SymbolTable::find_ConstVariable_inname(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            CLASS Class = (*it).second.first;
            if(Class != CLASS::CONST){
                return make_pair(TYPE::NEXIST, "");
            }
            int id = (*it).second.second;
            Sym_constVariable* constvariable = now->constVariableList[id];
            TYPE type = constvariable->typeName;
            string inname = constvariable->nameInComputer;
            PTS ans = make_pair(type, inname);
            return ans;
        }
    }  
    return make_pair(TYPE::NEXIST, "");
}

PTS SymbolTable::find_left_id(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it1 = now->fastPosition.find(name);
        bool not_find_var = (it1 == now->fastPosition.end());
        if(!not_find_var){
            CLASS Class = (*it1).second.first;
            int id = (*it1).second.second;
            if(Class == CLASS::VARIABLE){
                Sym_variable* variable = now->variableList[id];
                TYPE type = variable->typeName;
                string inname = variable->nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
            else{
                return make_pair(TYPE::NEXIST, "");
            }
        }
        for(auto it2 = now->parameterList.begin(); it2 != now->parameterList.end(); it2++){
            string id = (*it2)->name;
            if(id == name){
                TYPE type = (*it2)->typeName;
                string inname = (*it2)->nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
        }
        if(now == this){
            if(name == now->head){
                TYPE type = now->returnValue.typeName;
                string inname = now->returnValue.nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
        }
        now = now->preSymbolTable;
    }
    return make_pair(TYPE::NEXIST, "");
}

PTS SymbolTable::find_right_id(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it1 = now->fastPosition.find(name);
        bool not_find_var = (it1 == now->fastPosition.end());
        if(!not_find_var){
            CLASS Class = (*it1).second.first;
            int id = (*it1).second.second;
            if(Class == CLASS::VARIABLE){
                Sym_variable* variable = now->variableList[id];
                TYPE type = variable->typeName;
                string inname = variable->nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
            else if(Class == CLASS::CONST){
                Sym_constVariable* const_variable = now->constVariableList[id];
                TYPE type = const_variable->typeName;
                string inname = const_variable->nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
            else{
                return make_pair(TYPE::NEXIST, "");
            }
        }
        for(auto it2 = now->parameterList.begin(); it2 != now->parameterList.end(); it2++){
            string id = (*it2)->name;
            if(id == name){
                TYPE type = (*it2)->typeName;
                string inname = (*it2)->nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
        }
        if(now == this){
            if(name == now->head){
                TYPE type = now->returnValue.typeName;
                string inname = now->returnValue.nameInComputer;
                PTS ans = make_pair(type, inname);
                return ans;
            }
        }
        now = now->preSymbolTable;
    }
    return make_pair(TYPE::NEXIST, "");
}

// PTSI SymbolTable::find_array_inname(string name, vector<int>& index_list)
// {
//     SymbolTable* now = this;
//     while(now != nullptr){
//         auto it = now->fastPosition.find(name);
//         if(it == now->fastPosition.end()){
//             now = now->preSymbolTable;   
//         }
//         else{
//             int id = (*it).second.second;
//             Sym_array* array = now->arrayList[id];
//             PTS pts = make_pair(array->typeName, array->nameInComputer);
//             int index = array->get_index(index_list);
//             PTSI ans = make_pair(pts, index);
//             return ans;
//         }
//     }
//     return make_pair(make_pair(TYPE::NEXIST, ""), 0);
// }

PTS SymbolTable::find_array(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            CLASS Class = (*it).second.first;
            if(Class == CLASS::ARRAY){
                int id = (*it).second.second;
                Sym_array* array = now->arrayList[id];
                PTS ans = make_pair(array->typeName, array->nameInComputer);
                return ans;
            }
            else{
                return make_pair(TYPE::NEXIST, "");   
            }
        }
    }
    return make_pair(TYPE::NEXIST, "");    
}

Sym_array* SymbolTable::find_array_ptr(string name)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            CLASS Class = (*it).second.first;
            if(Class == CLASS::ARRAY){
                int id = (*it).second.second;
                Sym_array* array = now->arrayList[id];
                return array;
            }
            else{
                return nullptr;
            }
        }
    }
    return nullptr;    
}

PTSI SymbolTable::find_record(string name, vector<string>& list)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it == now->fastPosition.end()){
            now = now->preSymbolTable;   
        }
        else{
            CLASS Class = (*it).second.first;
            if(Class == CLASS::RECORD){
                int id = (*it).second.second;
                Sym_Record* record = now->recordList[id];
                int index;
                TYPE Type = record->find_member(list, index);
                PTS pts = make_pair(Type, record->nameInComputer);
                PTSI ans = make_pair(pts, index);
                return ans;
            }
            else{
                return make_pair(make_pair(TYPE::NEXIST, ""), 0);
            }
        }
    }
    return make_pair(make_pair(TYPE::NEXIST, ""), 0);
}

bool SymbolTable::find_parameter(string name)
{
    for(auto it = this->parameterList.begin(); it != this->parameterList.end(); it++){
        string id = (*it)->name;
        if(id == name){
            return true;
        }
    }
    return false;
}

TYPE SymbolTable::find_Function(string name, vector<TYPE>& parameter)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it != now->fastPosition.end()){
            CLASS Class = (*it).second.first;
            if(Class == CLASS::FUNCTION){
                int id = (*it).second.second;
                SymbolTable* function = now->functionList[id];
                if(parameter.size() != function->parameterList.size()){
                    return TYPE::NEXIST;
                }
                int num = (int) parameter.size();
                for(int i = 0; i < num; i++){
                    if(parameter[i] != function->parameterList[i]->typeName){
                        return TYPE::NEXIST;
                    }
                }
                return function->returnValue.typeName;
            }
            else{
                return TYPE::NEXIST;
            }
        }
        else if(now == this){
            if(name == now->head){//递归函数
                if(now->returnValue.typeName == TYPE::NEXIST){
                    return TYPE::NEXIST;
                }
                else{
                    if(parameter.size() != now->parameterList.size()){
                        return TYPE::NEXIST;
                    }
                    int num = (int) parameter.size();
                    for(int i = 0; i < num; i++){
                        if(parameter[i] != now->parameterList[i]->typeName){
                            return TYPE::NEXIST;
                        }
                    }
                    return now->returnValue.typeName;
                }
            }
            else{
                now = now->preSymbolTable;   
            }
        }
        else{
            now = now->preSymbolTable;   
        }
    }
    return TYPE::NEXIST;    
}

bool SymbolTable::find_Procedure(string name, vector<TYPE>& parameter)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it != now->fastPosition.end()){
            CLASS Class = (*it).second.first;
            if(Class == CLASS::PROCEDURE){
                int id = (*it).second.second;
                SymbolTable* procedure = now->procedureList[id];
                if(parameter.size() != procedure->parameterList.size()){
                    return false;
                }
                int num = (int) parameter.size();
                for(int i = 0; i < num; i++){
                    if(parameter[i] != procedure->parameterList[i]->typeName){
                        return false;
                    }
                }
                return true;
            }
            else{
                return false;
            }
        }
        else if(now == this){
            if(name == now->head){//递归过程
                if(now->returnValue.typeName != TYPE::NEXIST){
                    return false;
                }
                else{
                    if(parameter.size() != now->parameterList.size()){
                        return false;
                    }
                    int num = (int) parameter.size();
                    for(int i = 0; i < num; i++){
                        if(parameter[i] != now->parameterList[i]->typeName){
                            return false;
                        }
                    }
                    return true;
                }
            }
            else{
                now = now->preSymbolTable;   
            }
        }
        else{
            now = now->preSymbolTable;   
        }
    }
    return false;
}

void SymbolTable::find_all_parameter(string name, vector<bool>& para_type)
{
    SymbolTable* now = this;
    while(now != nullptr){
        auto it = now->fastPosition.find(name);
        if(it != now->fastPosition.end()){
            CLASS Class = (*it).second.first;
            int id = (*it).second.second;
            SymbolTable* symboltable = nullptr;
            if(Class == CLASS::FUNCTION){
                symboltable = now->functionList[id];
            }
            else if(Class == CLASS::PROCEDURE){
                symboltable = now->procedureList[id];
            }
            if(symboltable == nullptr){
                return;
            }
            for(auto it = symboltable->parameterList.begin(); it != symboltable->parameterList.end(); it++){
                para_type.push_back((*it)->isReference);
            }
            return;
        }
        else if(now == this){
            if(name == now->head){//递归函数
                for(auto it = now->parameterList.begin(); it != now->parameterList.end(); it++){
                    para_type.push_back((*it)->isReference);
                }
                return;                
            }
            else{
                now = now->preSymbolTable;   
            }         
        }
        else{
            now = now->preSymbolTable;   
        }
    }
}

/*
shared_ptr<SymType> SymbolTable::find_type(const string& typeName) const {
    auto cur = this;
    while (cur) {
        auto target = cur->typeMap.find(typeName);
        if (target != this->typeMap.end()) {
            return target->second;
        }
        else {
            cur = cur->preSymbolTable;
        }
    }
    return shared_ptr<SymType>();
}

// 如果没有存在就直接插入，否则赋予新值，和insert_or_assign执行相同作用
void SymbolTable::add_type(const string& typeName, shared_ptr<SymType>& type) {
    this->typeMap.insert_or_assign(typeName, type);
}

void SymbolTable::add_type(const string& lval, const string& rval) {
    this->typeMap.insert_or_assign(lval, this->find_type(rval));
}
*/

static bool is_number(const std::string& s)
{
    return !s.empty() && std::find_if(s.begin(), 
        s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}

pair<Offset, TYPE> SymStdType::get_var(vector<string>::iterator begin, vector<string>::iterator end) const {
    return make_pair(Offset(), typeName);
}

pair<Offset, TYPE> SymRecordType::get_var(vector<string>::iterator begin, vector<string>::iterator end) const {
    if (begin != end) {
        auto key = *begin;
        begin++;
        for (auto tp : record) {
            if (key == std::get<0>(tp)) {
                std::string name;
                size_t offset;
                SymType *sym_type;
                std::tie(name, sym_type, offset) = tp;
                auto var = sym_type->get_var(begin, end);
                var.first += offset;
                return var;
            }
        }
        // 错误处理

    }
    else {
        // 错误处理

    }
    return make_pair(Offset(), TYPE::NEXIST);
}

pair<Offset, TYPE> SymArrayType::get_var(vector<string>::iterator begin, vector<string>::iterator end) const {
    vector<string> indies(dims.size());
    // 插入到indies中
    // 检查indies中是否满足要求(未实现)
    for (auto i = 0; i < dims.size(); i++) {
        indies[i] = *begin;
        begin++;
    }

    // 求解偏移量offset最终满足如下规则，注意要求的是开始的地址，即a[1..2]，a[t]的首地址是(t-1) * size
    // f = (\sum_{i=0}^{k} t_i\prod_{j=i+1}^{k}n_j - \sum_{i=0}^{k} low_i\prod_{j=i+1}^{k}n_j) * size
    // 其中k代表dims.size(), n_j代表第j维的元素个数, 即up_j - low_j + 1
    size_t cst = lost;
    std::vector<std::pair<std::string, size_t>> coe;
    for (int i = dims.size() - 1; i >= 0; i--) {
        if (is_number(indies[i])) {
            // 加到常数里
            cst += std::stoi(indies[i]) * prod[i];
        }
        else {
            // 作为未计算出的多项式存储
            coe.push_back(std::make_pair(std::move(indies[i]), prod[i]));
        }
    }
    auto var = type->get_var(begin, end);
    var.first += cst;
    var.first += std::move(coe);

    return var;
}

TYPE Sym_Record::find_member(vector<string>& list, int& index) 
{
    pair<Offset, TYPE> var = record_type->get_var(list.begin(), list.end());
    index = var.first.cst;
    return var.second;
} 
